﻿using Antlr4.Runtime;
using System;
using System.Collections.Generic;
using System.Text;

namespace test
{
    class dumptool
    {
        public class ParserInfo
        {
            public Type type_lexer
            {
                get;
                private set;
            }
            public Type type_parser
            {
                get;
                private set;
            }
            public System.Reflection.MethodInfo exprmethod;
            public ParserInfo(Type lexer, Type parser, string exprmethod)
            {
                this.type_lexer = lexer;
                this.type_parser = parser;
                this.exprmethod = type_parser.GetMethod(exprmethod);
            }
            public static string CalcHashString(Type lexer, Type parser, string exprmethod)
            {
                string hash = lexer.FullName + ":" + parser.FullName + ":" + exprmethod;
                return hash;
            }

            public Antlr4.Runtime.RuleContext Parse(string text, bool dumpSrc, bool dumpLex, bool dumpAst)
            {
                bool bError = false;
                Antlr4.Runtime.AntlrInputStream input = new Antlr4.Runtime.AntlrInputStream(text);
                var lex = Activator.CreateInstance(type_lexer, input) as Lexer;
                //var lex = new ttparser.math4Lexer(input);
                if (dumpSrc)
                {
                    Console.WriteLine("==src==");
                    Console.WriteLine(text);
                }
                if (dumpLex)
                {
                    //直接操作lex 会影响后面的parser
                    Antlr4.Runtime.AntlrInputStream inputDump = new Antlr4.Runtime.AntlrInputStream(text);

                    //var lexDump = new ttparser.math4Lexer(input);
                    var lexDump = Activator.CreateInstance(type_lexer, inputDump) as Lexer;
                    Console.WriteLine("==dump lex==");
                    var alltoken = lexDump.GetAllTokens();
                    foreach (var t in alltoken)
                    {
                        var strtype = lex.Vocabulary.GetDisplayName(t.Type);
                        if (strtype == "UNKNOWN__")
                            bError = true;
                        Console.WriteLine("t=" + strtype + ":" + t.Text + "(" + t.Line + "," + t.Column + ")");
                    }
                }

                var tokenstream = new Antlr4.Runtime.CommonTokenStream(lex);
                //var parser = new ttparser.math4Parser(tokenstream);
                var parser = Activator.CreateInstance(type_parser, tokenstream) as Parser;
                //生成代码时，直接将parser的规则 expr 生成了函数
                //var exprroot = parser.root();
                var exprroot = exprmethod.Invoke(parser, null) as Antlr4.Runtime.ParserRuleContext;

                if (dumpAst)
                {
                    Console.WriteLine("==dump ast==");
                    DumpAst(lex, parser, exprroot);
                }

                if (exprroot.exception != null)
                {
                    Console.WriteLine("==err at:" + "(" + exprroot.exception.OffendingToken.Line + "," + exprroot.exception.OffendingToken.Column + ")" + exprroot.ToString());
                    throw exprroot.exception;

                }
                if (bError)
                    throw new Exception("had unknown");
                return exprroot;
            }
        }
        static Dictionary<string, ParserInfo> parsers = new Dictionary<string, ParserInfo>();

        public static ParserInfo GetParserInfo<T_LEX, T_PARSER>(string exprmethod)
             where T_LEX : Lexer where T_PARSER : Parser
        {
            var hashstr = ParserInfo.CalcHashString(typeof(T_LEX), typeof(T_PARSER), exprmethod);
            if (parsers.ContainsKey(hashstr) == false)
                parsers[hashstr] = new ParserInfo(typeof(T_LEX), typeof(T_PARSER), exprmethod);

            return parsers[hashstr];
        }



        static void DumpAst(Lexer lexer, Antlr4.Runtime.Parser parser, Antlr4.Runtime.Tree.IParseTree node, int deep = 0, List<int> linestyle = null)
        {
            var space = "";
            for (var i = 0; i < deep; i++)
            {

                space += " ";
                if (linestyle[i] > 0)
                {
                    if (i == deep - 1)
                    {
                        if (linestyle[i] == 1)
                            space += "|--";
                        else if (linestyle[i] == 2)
                        {
                            space += "\\--";
                            linestyle[i] = 0;
                        }
                    }
                    else
                        space += "|  ";
                }
                else
                {
                    space += "   ";
                }
            }
            if (deep > 0)
            {

            }
            var cont = node as Antlr4.Runtime.RuleContext;
            if (cont == null)
            {
                var syn = node as Antlr4.Runtime.Tree.ITerminalNode;
                if (syn != null)
                {

                    var token = lexer.Vocabulary.GetDisplayName(syn.Symbol.Type);
                    Console.WriteLine(space + token + ":" + syn.Symbol.Text);
                }
                else
                    Console.WriteLine(space + node.GetText());
            }
            else
            {
                if (deep == 0)
                {
                    linestyle = new List<int>();
                }
                linestyle.Add(node.ChildCount > 1 ? 1 : 0);
                var name = parser.RuleNames[cont.RuleIndex];
                Console.WriteLine(space + name);
                for (var i = 0; i < node.ChildCount; i++)
                {
                    Antlr4.Runtime.Tree.IParseTree child = node.GetChild(i);
                    if (i == node.ChildCount - 1)
                    {
                        if (linestyle[deep] == 1)
                            linestyle[deep] = 2;
                    }
                    DumpAst(lexer, parser, child, deep + 1, linestyle);
                }
                linestyle.RemoveAt(deep);
            }

        }

    }
}
